<template>
  <ElForm size="mini" ref="form" class="fa-widget-fill fa-contract-scope" :model="model" @submit.native.prevent>
    <slot name="start" v-bind="{ list: widgets, total, required, verified }" />

    <WidgetFillItem
      v-for="item in widgets"
      :key="item.id"
      :data="item"
      :viewer="viewer"
      @click.native="focusWidget(item)"
    >
      <slot :name="`${item.option.key}`" v-bind="{ data: item }" />
    </WidgetFillItem>

    <slot v-bind="{ list: widgets, total, required, verified }" />
  </ElForm>
</template>

<script lang="ts">
import { computed, defineComponent, getCurrentInstance, onUnmounted, PropType, provide, ref, watch } from 'vue'
import { Widget } from '@/models'
import { Viewer, WidgetMode, WidgetError } from '@/types'
import { ElForm } from '@/types/thirdParty'
import { scrollIntoView } from '@/utils'
import WidgetFillItem from './WidgetFillItem.vue'

/** 控件填写 */
export default defineComponent({
  name: 'FaWidgetFill',

  components: {
    WidgetFillItem
  },

  props: {
    /**
     * 文档查看组件
     * @description 文档查看组件提供已排序的填写控件列表、控件定位功能
     */
    viewer: Object as PropType<Viewer>,

    /** 控件过滤，默认使用中的填写控件 */
    filter: {
      type: Function as PropType<(widget: Widget) => boolean>,
      default: (item: Widget) => item.mode === WidgetMode.USE
    }
  },

  emits: {
    /** 触发校验 */
    'trigger-validate': () => undefined,

    /** 清除校验 */
    'clear-validate': () => undefined,

    /** 控件字段点击 */
    'field-click': (widget: Widget, e: MouseEvent) => undefined
  },

  setup(props, { emit }) {
    provide('WidgetFill', getCurrentInstance()?.proxy)

    /** 填写控件列表 */
    const widgets = computed(() => {
      // 过滤填写控件
      let list = props.viewer?.sortedWidgets.filter(item => item.isFill) || []

      // 自定义过滤
      return list.filter(widget => props.filter?.(widget as Widget))
    })

    /** 表单控件 */
    const form = ref(null as null | ElForm)

    /** 滚动到控件 */
    function scrollToWidget(widget: Widget, focus = false) {
      const scroller = form.value?.$el
      const widgetEl = scroller?.querySelector(`[widget-id="${widget.id}"]`)

      if (scroller && widgetEl) {
        scrollIntoView(widgetEl as HTMLElement, scroller as HTMLElement)

        if (focus) {
          const input = widgetEl.querySelector(`input, textarea`) as HTMLInputElement | null

          input?.focus()
        }
      }
    }

    function onWidgetClick(widget: Widget, e: MouseEvent) {
      scrollToWidget(widget)
    }

    /** 合同控件点击滚动定位 */
    watch(
      () => props.viewer,
      viewer => {
        if (!viewer) return
        viewer.$off('widget-click', onWidgetClick)
        viewer.$on('widget-click', onWidgetClick)
      }
    )

    onUnmounted(() => props.viewer?.$off('widget-click', onWidgetClick))

    return {
      /** 表单控件 */
      form,

      /** 填写控件 */
      widgets,

      /** 总数量 */
      total: computed(() => widgets.value.length),

      /** 必填数量 */
      required: computed(() => widgets.value.filter(widget => widget.required).length),

      /** 通过校验的数量 */
      verified: computed(() => widgets.value.filter(widget => !widget.errorUsing).length),

      /** 表单模型 */
      model: computed(() =>
        widgets.value?.reduce((map, widget) => {
          map[widget.id] = widget.value
          return map
        }, {} as Record<string, string | boolean>)
      ),

      /** 聚焦控件 */
      async focusWidget(widget: Widget) {
        props.viewer?.focusWidget(widget.id, false)
      },

      /** 触发校验,定位到首个错误的控件 */
      validate() {
        form.value?.validate().catch(e => {})

        /** 未通过校验的控件 */
        const invalidWidgets = widgets.value.filter(item => !!item.errorUsing)
        const firstInvalid = invalidWidgets[0]

        emit('trigger-validate')

        if (!firstInvalid) return

        const { viewer } = props

        viewer && viewer.focusWidget(firstInvalid.id, false)

        scrollToWidget(firstInvalid, true)

        throw {
          name: 'FaContract:validate',
          message: firstInvalid.errorUsing,
          invalidWidgets
        } as WidgetError
      },

      /** 清除校验 */
      clearValidate() {
        form.value?.clearValidate()
        emit('clear-validate')
      }
    }
  }
})
</script>
